package com.example.netchat;

import static com.example.netchat.CommonUtilities.SENDER_ID;
import static com.example.netchat.CommonUtilities.SERVER_URL;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;

import org.apache.http.client.methods.HttpPost;
import org.apache.http.util.EntityUtils;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.http.AndroidHttpClient;
import android.os.AsyncTask;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;

import com.example.netchat.ContentUtils.Room;
import com.google.android.gcm.GCMRegistrar;

public class ChatService extends Service {
	public class ChatServiceBinder extends Binder {

		ChatService getService() {
			return ChatService.this;
		}
	}

	public static final String MESSAGE_ARRIVED_ACTION = "com.example.netchat.NEW_MESSAGE";
	protected static final String USER_AGENT = "com.example.netchat";
	public static final String ROOM_ID_EXTRA = "room_id";
	public static final String FROM_EXTRA = "from";
	public static final String MESSAGE_EXTRA = "message";
	private static final String DATE_EXTRA = "date";
	private static final String DELETE_ROOM_ACTION = "com.example.netchat.DELETE_ROOM";
	private static final String ROOM_NAME_EXTRA = "room_name";
	private static final String CREATE_ROOM_ACTION = "com.example.netchat.CREATE_ROOM";
	public static final String HANDLE_INCOMING_MESSAGE_ACTION = "com.example.netchat.HANDLE_INCOMING_MESSAGE";
	protected static final String ID_EXTRA = "_id";
	private static final String SEND_MESSAGE_ACTION = "com.example.netchat.SEND_MESSAGE";
	protected static final String EXTRA_HANDLED = "handled";
	private static final String JOIN_ROOM_ACTION = "com.example.netchat.JOIN_ROOM";
	private static final String JOIN_ALL_ROOMS_ACTION = "com.example.netchat.JOIN_ALL_ROOMS";
	public static final String ROOM_DELETED_ACTION = "com.example.netchat.ROOM_DELETED";
	private static final String OLD_REG_ID_EXTRA = "old_reg_id";
	private final IBinder mBinder = new ChatServiceBinder();
	private boolean mRunning;

	@Override
	public IBinder onBind(Intent intent) {
		return this.mBinder;
	}

	@Override
	public void onDestroy() {
		GCMRegistrar.onDestroy(this);
		super.onDestroy();
	}

	private String getRegId() {
		return PreferenceManager.getDefaultSharedPreferences(this).getString(
				CommonUtilities.REG_PREF, "");
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		if (!mRunning) {
			mRunning = true;

			// Make sure the device has the proper dependencies.
			GCMRegistrar.checkDevice(this);
			// Make sure the manifest was properly set - comment out this
			// line
			// while developing the app, then uncomment it when it's ready.
			// GCMRegistrar.checkManifest(this);

			String regId = getRegId();

			if (regId.equals("")) {
				// Automatically registers application on startup.
				GCMRegistrar.register(this, SENDER_ID);
			}
		}

		if (intent == null || intent.getAction() == null) {
			return START_STICKY;
		}

		if (intent.getAction().equals(MESSAGE_ARRIVED_ACTION)) {
			messageArrived(intent.getStringExtra(ROOM_ID_EXTRA),
					intent.getStringExtra(FROM_EXTRA),
					intent.getStringExtra(MESSAGE_EXTRA),
					intent.getLongExtra(DATE_EXTRA, 0));
		} else if (intent.getAction().equals(DELETE_ROOM_ACTION)) {
			deleteRoom(intent.getLongExtra(ROOM_ID_EXTRA, 0));
		} else if (intent.getAction().equals(CREATE_ROOM_ACTION)) {
			createRoom(intent.getStringExtra(ROOM_NAME_EXTRA));
		} else if (intent.getAction().equals(SEND_MESSAGE_ACTION)) {
			sendMessage(intent.getStringExtra(ROOM_ID_EXTRA),
					intent.getStringExtra(MESSAGE_EXTRA),
					intent.getStringExtra(FROM_EXTRA));
		} else if (intent.getAction().equals(JOIN_ROOM_ACTION)) {
			joinRoom(intent.getStringExtra(ROOM_ID_EXTRA),
					intent.getStringExtra(ROOM_NAME_EXTRA));
		} else if (intent.getAction().equals(JOIN_ALL_ROOMS_ACTION)) {
			sendJoinRooms(intent.getStringExtra(OLD_REG_ID_EXTRA));
		}

		return START_STICKY;
	}

	private void joinRoom(String roomId, String name) {
		Room room = ContentUtils.getRoomByRoomId(getApplicationContext(),
				roomId);

		if (room == null) {
			room = new Room();
		}

		room.name = name;
		room.room_id = roomId;

		ContentUtils.updateRoom(getApplicationContext(), room);

		sendJoinRoom(getRegId(), roomId);

		startActivity(ChatActivity.createChatIntent(getApplicationContext(),
				roomId));
	}

	private void deleteRoom(final long id) {
		Room room = ContentUtils.getRoomById(getApplicationContext(), id);
		ContentUtils.deleteRoom(this, id);

		if (room != null) {
			sendLeaveRoom(room.room_id, getRegId());
		}

		sendBroadcast(new Intent(ROOM_DELETED_ACTION).putExtra(ID_EXTRA, id));
	}

	private void sendLeaveRoom(String roomId, String regId) {
		new AsyncTask<String, Void, Void>() {

			@Override
			protected Void doInBackground(String... params) {
				String roomId = params[0];
				String regId = params[1];

				AndroidHttpClient client = AndroidHttpClient
						.newInstance(USER_AGENT);

				try {
					HttpPost post = new HttpPost(SERVER_URL
							+ "/rooms/join?roomId="
							+ URLEncoder.encode(roomId, "utf-8") + "&regId="
							+ regId);

					client.execute(post);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					client.close();
				}
				return null;
			}

		}.execute(roomId, regId);
	}

	protected void sendJoinRooms(String oldRegId) {
		final String regId = getRegId();
		List<String> roomIds = ContentUtils
				.getAllRoomIdsWithDifferentLastRegId(this, regId);

		for (final String roomId : roomIds) {
			if (oldRegId != null && !oldRegId.equals("")) {
				sendLeaveRoom(roomId, oldRegId);
			}

			sendJoinRoom(regId, roomId);
		}
	}

	private void sendJoinRoom(final String regId, final String roomId) {
		new AsyncTask<String, Void, Void>() {
			@Override
			protected Void doInBackground(String... params) {
				String roomId = params[0];
				String regId = params[1];

				AndroidHttpClient client = AndroidHttpClient
						.newInstance(USER_AGENT);

				try {
					HttpPost post = new HttpPost(SERVER_URL
							+ "/rooms/join?roomId="
							+ URLEncoder.encode(roomId, "utf-8") + "&regId="
							+ regId);

					client.execute(post);

					Room room = ContentUtils.getRoomByRoomId(
							getApplicationContext(), roomId);

					room.last_reg_id = regId;

					ContentUtils.updateRoom(getApplicationContext(), room);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					client.close();
				}
				return null;
			}
		}.execute(roomId, regId);
	}

	public void createRoom(final String roomName) {
		new AsyncTask<Void, Void, Void>() {
			@Override
			protected Void doInBackground(Void... params) {
				AndroidHttpClient client = AndroidHttpClient
						.newInstance(USER_AGENT);

				try {
					HttpPost post = new HttpPost(SERVER_URL
							+ "/rooms/create?roomName="
							+ URLEncoder.encode(roomName, "utf-8"));

					String result = EntityUtils.toString(client.execute(post)
							.getEntity());

					String regId = getRegId();
					post = new HttpPost(SERVER_URL + "/rooms/join?roomId="
							+ result + "&regId="
							+ URLEncoder.encode(regId, "utf-8"));

					client.execute(post);

					Room room = new Room();

					room.name = roomName;
					room.room_id = result;
					room.favorite = false;

					ContentUtils.updateRoom(ChatService.this, room);

					sendJoinRoom(regId, room.room_id);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					client.close();
				}

				return null;
			}
		}.execute(null, null);
	}

	private void messageArrived(final String roomId, final String from,
			final String message, final long date) {
		if (roomId == null || from == null || message == null) {
			return;
		}

		final Room room = ContentUtils.getRoomByRoomId(this, roomId);

		if (room == null) {
			sendLeaveRoom(roomId, getRegId());
			return;
		}

		final String roomName = room.name;

		ContentUtils.saveMessage(this, roomId, from, message, new Date(date));

		Intent intent = new Intent(HANDLE_INCOMING_MESSAGE_ACTION);
		intent.putExtra(ID_EXTRA, room._id);
		intent.putExtra(ROOM_ID_EXTRA, roomId);
		intent.putExtra(ROOM_NAME_EXTRA, roomName);
		intent.putExtra(FROM_EXTRA, from);
		intent.putExtra(MESSAGE_EXTRA, message);
		intent.putExtra(DATE_EXTRA, date);

		sendStickyOrderedBroadcast(intent, new BroadcastReceiver() {

			@Override
			public void onReceive(Context context, Intent intent) {
				if (this.getResultCode() != Activity.RESULT_OK) {
					generateNotification(getApplicationContext(), message,
							from, room);
				}
			}
		}, null, Activity.RESULT_CANCELED, null, null);
	}

	private static void generateNotification(Context context, String message,
			String from, Room room) {
		int icon = R.drawable.ic_conversation;
		long when = System.currentTimeMillis();
		NotificationManager notificationManager = (NotificationManager) context
				.getSystemService(Context.NOTIFICATION_SERVICE);
		Intent notificationIntent = ChatActivity.createChatIntent(context,
				room.room_id);
		// set intent so it does not start a new activity
		notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
				| Intent.FLAG_ACTIVITY_SINGLE_TOP);
		PendingIntent intent = PendingIntent.getActivity(context, 0,
				notificationIntent, 0);

		Notification notification = new NotificationCompat.Builder(context)
				.setContentTitle(
						context.getString(R.string.message_notification_title,
								room.name))
				.setContentText(
						context.getString(
								R.string.message_notification_content, from,
								message)).setAutoCancel(true)
				.setSmallIcon(icon).setWhen(when).setContentIntent(intent)
				.build();

		notificationManager.notify(R.id.chat_notification + (int) room._id,
				notification);
	}

	public void sendMessage(final String roomId, final String message,
			final String nick) {
		new AsyncTask<Void, Void, Void>() {
			@Override
			protected Void doInBackground(Void... params) {
				AndroidHttpClient client = AndroidHttpClient
						.newInstance(USER_AGENT);

				try {
					HttpPost post = new HttpPost(SERVER_URL + "/send?roomId="
							+ roomId + "&message="
							+ URLEncoder.encode(message, "utf-8") + "&nick="
							+ URLEncoder.encode(nick, "utf-8"));

					client.execute(post);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} finally {
					client.close();
				}
				return null;
			}
		}.execute(null, null);
	}

	public static Intent createRoomDeleteIntent(Context context, long id) {
		Intent intent = new Intent(context, ChatService.class);

		intent.setAction(DELETE_ROOM_ACTION);
		intent.putExtra(ROOM_ID_EXTRA, id);

		return intent;
	}

	public static Intent createMessageArrivedIntent(Context context,
			String roomId, String from, String message, Date date) {
		Intent intent = new Intent(context, ChatService.class);

		intent.setAction(MESSAGE_ARRIVED_ACTION);
		intent.putExtra(ROOM_ID_EXTRA, roomId);
		intent.putExtra(FROM_EXTRA, from);
		intent.putExtra(MESSAGE_EXTRA, message);
		intent.putExtra(DATE_EXTRA, date.getTime());

		return intent;
	}

	public static Intent createRoomCreateIntent(Context context, String roomName) {
		Intent intent = new Intent(context, ChatService.class);

		intent.setAction(CREATE_ROOM_ACTION);
		intent.putExtra(ROOM_NAME_EXTRA, roomName);

		return intent;
	}

	public static Intent createSendMessageIntent(Context context,
			String roomId, String message, String nick) {
		Intent intent = new Intent(context, ChatService.class);

		intent.setAction(SEND_MESSAGE_ACTION);
		intent.putExtra(ROOM_ID_EXTRA, roomId);
		intent.putExtra(MESSAGE_EXTRA, message);
		intent.putExtra(FROM_EXTRA, nick);

		return intent;
	}

	public static Intent createJoinRoomIntent(Context context, String roomId,
			String roomName) {
		Intent intent = new Intent(context, ChatService.class);
		intent.setAction(JOIN_ROOM_ACTION);

		intent.putExtra(ROOM_ID_EXTRA, roomId);
		intent.putExtra(ROOM_NAME_EXTRA, roomName);

		return intent;
	}

	public static Intent createJoinAllRoomsIntent(Context context,
			String oldRegId) {
		return new Intent(context, ChatService.class).putExtra(
				OLD_REG_ID_EXTRA, oldRegId).setAction(JOIN_ALL_ROOMS_ACTION);
	}
}
